#include "stdafx.h"
#include "TimeSpec.h"
#include <assert.h>

namespace serika
{
	namespace ut
	{
		const long ONE_SEC_IN_NSEC = 1000000000L;
		const long ONE_MSEC_IN_NSEC = 1000000L;
	
		TimeSpec::TimeSpec()
		{
			tv_sec = tv_nsec = 0;
		}
	
		TimeSpec::TimeSpec(const TimeSpec& other)
		{
			tv_sec = other.tv_sec;
			tv_nsec = other.tv_nsec;
		}
	
		const TimeSpec& TimeSpec::Now(int clockid)
		{
			static TimeSpec ts;
	#ifdef WIN32
			FILETIME ftime;
			GetSystemTimeAsFileTime(&ftime);
			const INT64 skew = 0x19db1ded53e8000i64;
			ULARGE_INTEGER _100ns;
			_100ns.LowPart = ftime.dwLowDateTime;
			_100ns.HighPart = ftime.dwHighDateTime;
			_100ns.QuadPart -= skew;
	
			// Convert 100ns units to seconds;
			ts.tv_sec = (long) (_100ns.QuadPart / (10000 * 1000));
			// Convert remainder to microseconds;
			ts.tv_nsec = (long) ((_100ns.QuadPart % (10000 * 1000)) * 100);
	
			ts.Normalize();
	#elif defined(__APPLE__) && defined(__MACH__)
			assert(false);
	#else
			clock_gettime(clockid, &ts);
	#endif
			return ts;
		}
	
		const TimeSpec& TimeSpec::MakeTimeout(long msec, int clockid)
		{
			static TimeSpec ts;
	#ifdef WIN32
			FILETIME ftime;
			GetSystemTimeAsFileTime(&ftime);
			const INT64 skew = 0x19db1ded53e8000i64;
			ULARGE_INTEGER _100ns;
			_100ns.LowPart = ftime.dwLowDateTime;
			_100ns.HighPart = ftime.dwHighDateTime;
			_100ns.QuadPart -= skew;
	
			// Convert 100ns units to seconds;
			ts.tv_sec = (long) (_100ns.QuadPart / (10000 * 1000));
			// Convert remainder to microseconds;
			ts.tv_nsec = (long) ((_100ns.QuadPart % (10000 * 1000)) / 10);
	
			ts.Normalize();
	#elif defined(__APPLE__) && defined(__MACH__)
			assert(false);
	#else
			clock_gettime(clockid, &ts);
	#endif
			ts.tv_nsec += msec * ONE_MSEC_IN_NSEC;
			ts.tv_sec += ts.tv_nsec / ONE_SEC_IN_NSEC;
			ts.tv_nsec %= ONE_SEC_IN_NSEC;		
			return ts;
		}
	
		double TimeSpec::ToSeconds() const
		{
			return tv_sec + (double)tv_nsec / ONE_SEC_IN_NSEC;
		}
	
		void TimeSpec::Reload()
		{
			(*this) = TimeSpec::Now();
		}
	
		bool TimeSpec::operator < (const TimeSpec& other) const
		{
			if (tv_sec < other.tv_sec)
			{
				return true;
			}
			else if (tv_sec == other.tv_sec)
			{
				return tv_nsec < other.tv_nsec;
			}
			else
			{
				return false;
			}
		}
	
		bool TimeSpec::operator == (const TimeSpec& other) const
		{
			return tv_sec == other.tv_sec && tv_nsec == other.tv_nsec;
		}
	
		bool TimeSpec::operator != (const TimeSpec& other) const
		{
			return !(*this == other);
		}
	
		bool TimeSpec::operator > (const TimeSpec& other) const
		{
			return !(*this < other || *this == other);
		}
	
		bool TimeSpec::operator <= (const TimeSpec& other) const
		{
			return *this < other || *this == other;
		}
	
		bool TimeSpec::operator >= (const TimeSpec& other) const
		{
			return *this > other || *this == other;
		}
	
		TimeSpec& TimeSpec::operator = (const TimeSpec& other)
		{
			if (&other == this)
			{
				return *this;
			}
			tv_sec = other.tv_sec;
			tv_nsec = other.tv_nsec;
			return *this;
		}
	
		TimeSpec TimeSpec::operator - (const TimeSpec& other) const
		{
			TimeSpec ret;
			ret.tv_sec = tv_sec - other.tv_sec;
			ret.tv_nsec = tv_nsec - other.tv_nsec;
			ret.Normalize();
			return ret;
		}
	
		TimeSpec TimeSpec::operator + (const TimeSpec& other) const
		{
			TimeSpec ret;
			ret.tv_sec = tv_sec + other.tv_sec;
			ret.tv_nsec = tv_nsec + other.tv_nsec;
			ret.Normalize();
			return ret;
		}
	
		TimeSpec& TimeSpec::operator -= (const TimeSpec& other)
		{
			tv_sec -= other.tv_sec;
			tv_nsec -= other.tv_nsec;
			this->Normalize();
			return *this;
		}
	
		TimeSpec& TimeSpec::operator += (const TimeSpec& other)
		{
			tv_sec += other.tv_sec;
			tv_nsec += other.tv_nsec;
			this->Normalize();
			return *this;
		}
	
		inline void TimeSpec::Normalize()
		{
			if (tv_nsec >= ONE_SEC_IN_NSEC)
			{
				do
				{
					++tv_sec;
					tv_nsec -= ONE_SEC_IN_NSEC;
				}
				while (tv_nsec >= ONE_SEC_IN_NSEC);
			}
			else if (tv_nsec <= -ONE_SEC_IN_NSEC)
			{
				do
				{
					--tv_sec;
					tv_nsec += ONE_SEC_IN_NSEC;
				}
				while (tv_nsec <= -ONE_SEC_IN_NSEC);
			}
	
			if (tv_sec >= 1 && tv_nsec < 0)
			{
				--tv_sec;
				tv_nsec += ONE_SEC_IN_NSEC;
			}
		}
	}
}
